Build source code
On April 1, 2003 3D Realms released the source code to Duke Nukem 3D after years of saying it would never happen. Not long afterwards both Icculus and JonoF had ports made. It was now possible to play Duke Nukem 3D well on the NT line of Windows (including Windows 2000/XP) and on Linux and other Unix Operating Systems, and interest in the ports soared. On 20th of June 2000 the build engine source code was released by Ken Silverman The following are the files included in this source code: ((explanation for every one needed)). =KENBUILD.ZIP= This file contains the game Ken's Labyrinth, with the Build engine, some maps and other files. It also contains the source code itself, wich is in the zipped file SRC.ZIP. To run Ken's Labyrinth on modern operative systems you may need an emulator like Dosbox and the little program dos/4gw. Maps *ASCBOARD.MAP *NSNOAL.MAP *BOARDS.MAP *EVILAL.MAP *KENSIG.MAP *NUKELAND.MAP Executables *GAME.EXE *SETUP.EXE Data files *STUFF.DAT *SETUP.DAT Text files *BUILDLIC.TXT *README.TXT Zipped files *SRC.ZIP *UTIL.ZIP = SRC.ZIP = C language source code *ENGINE.C *MULTI.C *BSTUB.C *BUILD.C *CACHE1D.C *GAME.C *KDMENG.C *MMULTI.C Header files *PRAGMAS.H *VES2.H *BUILD.H *NAMES.H Assembly language files *A.ASM *K.ASM Text files *BUILD2.TXT *BUILDINF.TXT *SREADME.TXT *BUILD.TXT Misc *MAKEFILE ((screenshots of Ken's Labyrinth needed)). = Important engine functions = These functions are inside the files ((files needed)). ((It would be interesting to illustrate every single function with pictures showing it's basic functionig in build)). initengine *'Arguments:' (char vidoption, long xdim, long ydim); *'Function:' Sets up interrupt vectors for keyboard, and initializes many variables for the BUILD engine. You should call this once before any other functions of the BUILD engine are used. *vidoption can be anywhere from 0-6 *xdim,ydim can be any mode x resolution if vidoption = 0 *xdim,ydim can be any vesa resolution if vidoption = 1 *xdim,ydim must be 320*200 for any other mode. (see graphics mode selection in the setup program) uninitengine *'Arguments:' (); (none) *'Function:' Restores interrupt vectors for keyboard and timer, and frees buffers. You should call this once at the end of the program before quitting to dos. loadboard *'Arguments:' (char *filename, long *posx, long *posy, long *posz, short *ang, short *cursectnum) *'Function:' Loads the given board file into memory for the BUILD engine. Returns -1 if file not found. If no extension is given, .MAP will be appended to the filename. saveboard *'Arguments:' (char *filename, long *posx, long *posy, long *posz, short *ang, short *cursectnum); *'Function:' Saves the given board from memory inro the specified filename. Returns -1 if unable to save. If no extension is given, .MAP will be appended to the filename. loadpics *'Arguments:' (char *filename); *'Function:' Loads the given artwork file into memory for the BUILD engine. Returns -1 if file not found. If no extension is given, .ART will be appended to the filename. setgamemode *'Arguments:' (); (none) *'Function:' This function sets the video mode to 320*200*256color graphics. Since BUILD supports several different modes including mode x, mode 13h, and other special modes, I don't expect you to write any graphics output functions. (Soon I have all the necessary functions) If for some reason, you use your own graphics mode, you must call this function again before using the BUILD drawing functions. drawrooms *'Arguments:' (long posx, long posy, long posz, short ang, long horiz, short cursectnum); *'Function:' This function draws the 3D screen to the current drawing page, which is not yet shown. This way, you can overwrite some things over the 3D screen such as a gun. Be sure to call the drawmasks() function soon after you call the drawrooms() function. To view the screen, use the nextpage() function. The nextpage() function should always be called sometime after each draw3dscreen() function. drawmasks *'Arguments:' (); (none) *'Function:' This function draws all the sprites and masked walls to the current drawing page which is not yet shown. The reason I have the drawing split up into these 2 routines is so you can animate just the sprites that are about to be drawn instead of having to animate all the sprites on the whole board. Drawrooms() prepares these variables: spritex[], spritey[], spritepicnum[], thesprite[], and spritesortcnt. Spritesortcnt is the number of sprites about to be drawn to the page. To change the sprite's picnum, simply modify the spritepicnum array If you want to change other parts of the sprite structure, then you can use the thesprite array to get an index to the actual sprite number. engineinput *'Arguments:' (); (none) *'Function:' This function allows the engine to adjust your position depending on the status of the arrow keys, and other control keys. It handles timing and clipping. nextpage *'Arguments:' (); (none) *'Function:' After a screen is prepared, use this function to view the screen. draw2dscreen *'Arguments:' (long posxe, long posye, short ange, long zoome, short gride); *'Function:' Draws the 2d screen - this function is a direct replacement for the drawrooms() and drawmasks() functions. Be sure to call either qsetmode640350() or qsetmode640480()first. When switching back to 3d mode, be sure to call qsetmode320200(). IMPORTANT NOTES: 1. The overwritesprite function should only be called in 3D mode. If you do this in 2D mode, junk will be written to the 2D screen and a crash is possible. 2. When you switch back to 3D mode, you should call the permanentwritesprite functions to draw the status bar, or whatever else you have to draw. 3. You must call the nextpage() function in both 2D and 3D modes. *qsetmode320200(); Set to the game mode and load palette (320*200*256) *qsetmode640350(); Set to the 2D map mode #1 (640*350*16) *qsetmode640480(); Set to the 2D map mode #2 (640*480*16) doanimations *'Arguments:' (long numtics); *'Function:' This function animates anything you use setanimation for (like doors). You should call it for every frame. Pass the number of tics (lockspeed) as a parameter to it to tell how much everything should animate. kenchaintimer *'Arguments:' (void (__interrupt __far *datimerchainaddress)(), short dachainpersecond); *'Function:' This function makes the engine's timerhandler chain to another timer handler at any specified interrupt rate. This function forces IRQ0 to point to my engine's timerhandler. Clockspeed and totalclock will be fixed at counting 120 per second regardless of the chaining interrupt rate. If you call this function with a NULL pointer, then the engine's timerhandler will not chain anymore. Here's how you should structure your code if you use this function: main(){ initengine(); musicon(); //Turn music on after engine kenchaintimer(yourtimerhandleraddress,yourtimerrate); //When IRQ0 goes off, it will now go to //Ken's timer handler. Then, Ken's timer (main loop) //handler will make yourtimerhandler //interrupt yourtimerrate times per second kenchaintimer(0,0); //Stop chaining BEFORE music handler dies! musicoff(); uninitengine(); } = Other important engine functions = overwritesprite *'Arguments:' (long thex, long they, short tilenum, signed char shade, char orientation, char dapalnum) *'Function:' Use this function to draw any sprites that must be drawn to the screen for every single frame, such as a gun or a menu system. *If Bit 0 of orientation = 0: (thex, they) is top-left corner *If Bit 0 of orientation = 1: (thex, they) is middle *If Bit 1 of orientation = 0: no relation to viewing window *If Bit 1 of orientation = 1: scale and clip to viewing window *If Bit 2 of orientation = 0: normal *If Bit 2 of orientation = 1: 50/50 transluscent! *If Bit 3 of orientation = 0: normal *If Bit 3 of orientation = 1: x-flipped *If Bit 4 of orientation = 0: normal *If Bit 4 of orientation = 1: y-flipped *If it works at full screen, simply set bit 1 of orientation to 1, and it should automatically scale properly! Use this function to write sprites over the 3d view. For example, you can make a menu system with this function. Be sure that you call this function for every single frame after the 3d view is drawn or else it will be flashed on for only 1 frame. If you want x and y to be the top left corner, set the orientation to 0. If you want x and y to be the middle of the sprite, set the orientation to 1. The reason I included the orienation = 1 option is so that if you want a sprite centered and the size of the tile changes, you don't need to recompile and guess where the new top left corner is. Oh yeah, and I forget to mention that if shade is greater than 32, than overwritesprite does transluscence. (Try it out!) This function will clip the sprite to the startumost and startdmost arrays. Dapalnum refers to a palette lookup list (normally 0). rotatesprite *'Arguments:' (long sx, long sy, long z, short a, short picnum, signed char dashade, char dapalnum, char dastat,long cx1, long cy1, long cx2, long cy2) *'Function:' *(sx, sy) is the center of the sprite to draw defined as screen coordinates shifted up by 16. *(z) is the zoom. Normal zoom is 65536. *Ex: 131072 is zoomed in 2X and 32768 is zoomed out 2X. *(a) is the angle (0 is straight up) *(picnum) is the tile number *(dashade) is 0 normally but can be any standard shade up to 31 or 63. *(dapalnum) can be from 0-255. *if ((dastat&1) 0) - no transluscence *if ((dastat&1) != 0) - transluscence *if ((dastat&2) 0) - don't scale to setview's viewing window *if ((dastat&2) != 0) - scale to setview's viewing window (windowx1,etc.) *if ((dastat&4) 0) - nuttin' special *if ((dastat&4) != 0) - y-flip image *if ((dastat&8) 0) - clip to startumost/startdmost *if ((dastat&8) != 0) - don't clip to startumost/startdmost *if ((dastat&16) 0) - use Editart center as point passed *if ((dastat&16) != 0) - force point passed to be top-left corner *if ((dastat&32) 0) - nuttin' special *if ((dastat&32) != 0) - use reverse transluscence *if ((dastat&64) 0) - masked drawing (check 255's) (slower) *if ((dastat&64) != 0) - draw everything (don't check 255's) (faster) Note: As a special case, if both ((dastat&2) != 0) and ((dastat&8) != 0) then rotatesprite will scale to the full screen (0,0,xdim-1,ydim-1) rather than setview's viewing window. (windowx1,windowy1,etc.) This case is useful for status bars, etc. Ex: rotatesprite(160L<<16,100L<<16,65536,totalclock<<4, DEMOSIGN,2,50L,50L,270L,150L); This example will draw the DEMOSIGN tile in the center of the screen and rotate about once per second. The sprite will only get drawn inside the rectangle from (50,50) to (270,150) permanentwritesprite *'Arguments:' (long thex, long they, short tilenum, signed char shade, long cx1, long cy1, long cx2, long cy2, char dapalnum); *'Function:' Added permanentwritesprite function for status bars or other sections of the screen that will not be overwritten by the engine. The format of this function is like overwritesprite except that the x and y are always top left corner, no orientation variable, and no translucence. The 4 last parameters (cx1, cy1) - (cx2, cy2) define a rectangular clipping window of where permanentwritesprite can draw to. Dapalnum refers to a palette lookup list (normally 0). printext *'Arguments:' (long x, long y, char buffer42, short tilenum, char invisiblecol); *'Function:' Use this function to print text anywhere on the screen from a font that you can create in EDITART. Please see my example font in TILES.ART to see how I lay out the user-defined font. X ranges from 0-319. Y ranges from 0-199. The buffer is the string to print. Tilenum specifies which font to use. Invisiblecol tells printext what color to draw the transparent pixels. If invisiblecol is 255 then the transpararent pixels are still transparent. printnum *'Arguments:' (long x, long y, long num, short tilenum, char invisiblecol); *'Function:' Printnum is a function call that will print a long integer (num) starting at top left corner x, y. Please look at the documentation for printext, since internally, printnum simply prepares a buffer and calls the printext function. setvmode *'Arguments:' (long videomode); *'Function:' If you look at the top of GAME.C, you will see something like this: #pragma aux setvmode =\... This is how you do in-line assembler in WATCOM C. All this function is doing is setting the video mode. showengineinfo *'Arguments:' (); (none) *'Function:' Use this function after setting to text mode to view some statics about the engine, such as frame rate. resettiming *'Arguments:' (); (none) *'Function:' Resets timing, such as setting totalclock = 0. Also resets other timers. This is for use with the showengineinfo function above. ksqrt *'Arguments:' (long num); returns (long)square root *'Function:' A square root function optimized for integers. Use this function only if you want to. krand *'Arguments:' (); (none) *'Function:' This simply returns a random number. You can easily set the random seed by externing the randomseed variable as a long. This is useful for keeping the random seed the same on multiple computers when playing multi-player mode. getangle *'Arguments:' (long xvect,long yvect); returns (short)angle; *'Function:' Use this function call to determine the angle between two points. For example, if you want a monster to shoot a bullet towards you, you would get the bullet's angle this way: spritebullet.ang = getangle(posx-spritemonst.x,posy-spritemonst.y); lastwall *'Arguments:' (short point); *'Function:' Use this function as a reverse function of wall[].point2. In order to save memory, my walls are only on a single linked list. rotatepoint *'Arguments:' (long xpivot, long ypivot, long x, long y, short daang, long *x2, long *y2); *'Function:' This function is a very convenient and fast math helper function. Rotate points easily with this function without having to juggle your cosines and sines. Simply pass it: *Input: *1. Pivot point (xpivot,ypivot) *2. Original point (x,y) *3. Angle to rotate (0 = nothing, 512 = 90ø CW, etc.) *Output: 4. Rotated point (*x2,*y2) clipmove *'Arguments:' (long *x, long *y, long *z, short *sectnum, long xvect, long yvect, long walldist, long ceildist, long flordist, char cliptype); *'Function:' Moves any object (x, y, z) in any direction at any velocity and will make sure the object will stay a certain distance from walls (walldist). Pass the pointers of the starting position (x, y, z). Then pass the starting position's sector number as a pointer also. Also these values will be modified accordingly. Pass the direction and velocity by using a vector (xvect, yvect). If you don't fully understand these equations, please call me. xvect = velocity * cos(angle) yvect = velocity * sin(angle) Walldist tells how close the object can get to a wall. I use 128L as my default. If you increase walldist all of a sudden for a certain object, the object might leak through a wall, so don't do that! If cliptype is 0, then the clipping is normal (Use 0 to clip you and monsters). If the cliptype is 1, then the object is clipped to the same things that hitscan is clipped to (use 1 for all bullets). Clipmove can either return 0 (touched nothing) 32768+wallnum (wall first touched) 49152+spritenum (sprite first touched) getzrange *'Arguments:' (long x, long y, long z, short sectnum, long *ceilz, long *ceilhit, long *florz, long *florhit, long walldist, char cliptype) *'Function:' Use this in conjunction with clipmove. This function will keep the player from falling off cliffs when you're too close to the edge. This function finds the highest and lowest z coordinates that your clipping BOX can get to. It must search for all sectors (and sprites) that go into your clipping box. This method is better than using sectorcursectnum.ceilingz and sectorcursectnum.floorz because this searches the whole clipping box for objects, not just 1 point. Pass x, y, z, sector normally. Walldist can be 128. Cliptype can be 0, 1, or 2. (just like movesprite and clipmove) This function returns the z extents in ceilz and florz. It will return the object hit in ceilhit and florhit. Ceilhit and florhit will also be either: 16384+sector (sector first touched) or 49152+spritenum (sprite first touched) updatesector *'Arguments:' (long x, long y, &sectnum); *'Function:' This function updates the sector number according to the x and y values passed to it. Be careful when you use this function with sprites because remember that the sprite's sector number should not be modified directly. If you want to update a sprite's sector, I recomment using the setsprite function described below. inside *'Arguments:' (long x, long y, short sectnum); *'Function:' Tests to see whether the overhead point (x, y) is inside sector (sectnum). Returns either 0 or 1, where 1 means it is inside, and 0 means it is not. copytilepiece *'Arguments:' (long tilenume1, long sourcex1, long sourcey1, long xsiz, long ysiz, long tilenume2, long destx1, long desty1) *'Function:' This function simply copies any section of a source tile to any part of a destination tile. It will automatically skip transparent pixels. It will wrap-around in the source but not the destination. If for some reason the destination tile gets removed from the cache, the destination tile will be reset to original form. This is why I had to add this second function: allocatepermanenttile *'Arguments:' (short tilenume, long xsiz, long ysiz); *'Function:' This function allocates a place on the cache as permanent. Right now, I reset the cache every time you call this function so I would recommend calling this function right after loadpics. makepalookup *'Arguments:' (long palnum, char *remapbuf, signed char r, signed char g, signed char b, char dastat); *'Function:' This function allows different shirt colors for sprites. First prepare remapbuf, which is a 256 byte buffer of chars which the colors to remap. Palnum can be anywhere from 1-15. Since 0 is where the normal palette is stored, it is a bad idea to call this function with palnum=0. In BUILD.H notice I added a new variable, spritepalMAXSPRITES. Usually the value of this is 0 for the default palette. But if you change it to the palnum in the code between drawrooms() and drawmasks then the sprite will be drawn with that remapped palette. The last 3 parameters are the color that the palette fades to as you get further away. This color is normally black (0,0,0). White would be (63,63,63). if ((dastat&1) 0) then makepalookup will allocate & deallocate the memory block for use but will not waste the time creating a palookup table (assuming you will create one yourself) copytilepiece *'Arguments:' (long walnume1, long x1, long y1, long xsiz, long ysiz, long walnume2, long x2, long y2, char shadeoffs); *'Function:' Copies section of tile 1 (walnume1) with top-left corner (x1,y1) and rectangular size (xsiz, ysiz) to top-left corner (x2, y2) of tile 2 (walnume). You can animate tiles with this function. For example, with this function, you can make a slot machine like in Ken's Labyrinth or an electronic sign with text sliding from right to left. loadtile *'Arguments:' (short tilenume); *'Function:' This function will load the tile, tilenum, into the artwork cache. A tile is not in the cache if (walofftilenum -1). If (walofftilenum >= 0) then it is in the cache, and you don't need to call this function. precache *'Arguments:' (); (none) *'Function:' This function will go through the tilenums of all sectors, walls, and sprites and call loadtile() on them. This function will not cache in some tiles of animations since their tilenums may not all be in the structures. hitscan *'Arguments:' (long xstart, long ystart, long zstart, short startsectnum, long vectorx, long vectory, long vectorz, short *hitsect, short *hitwall, short *hitsprite, long *hitx, long *hity, long *hitz); *'Function:' *Pass the starting 3D position: (xstart, ystart, zstart, startsectnum) *Then pass the 3D angle to shoot (defined as a 3D vector): (vectorx, vectory, vectorz) *Then set up the return values for the object hit: (hitsect, hitwall, hitsprite) *and the exact 3D point where the ray hits: (hitx, hity, hitz) How to determine what was hit: * Hitsect is always equal to the sector that was hit (always >= 0). * If the ray hits a sprite then: **hitsect = thesectornumber **hitsprite = thespritenumber **hitwall = -1 * If the ray hits a wall then: **hitsect = thesectornumber **hitsprite = -1 **hitwall = thewallnumber * If the ray hits the ceiling of a sector then: **hitsect = thesectornumber **hitsprite = -1 **hitwall = -1 **vectorz < 0 (If vectorz < 0 then you're shooting upward which means that you couldn't have hit a floor) * If the ray hits the floor of a sector then: **hitsect = thesectornumber **hitsprite = -1 **hitwall = -1 **vectorz > 0 *(If vectorz > 0 then you're shooting downard which means that you couldn't have hit a ceiling) neartag *'Arguments:' *long x, long y, long z, short sectnum, short ang, //Starting position & angle *short *neartagsector, //Returns near sector if sector[].tag != 0 *short *neartagwall, //Returns near wall if wall[].tag != 0 *short *neartagsprite, //Returns near sprite if sprite[].tag != 0 *long *neartaghitdist, //Returns actual distance to object (scale: 1024=largest grid size) *long neartagrange, //Choose maximum distance to scan (scale: 1024=largest grid size) *char tagsearch) //1-lotag only, 2-hitag only, 3-lotag&hitag *'Function:' Neartag works sort of like hitscan, but is optimized to scan only close objects and scan only objects with tags != 0. Neartag is perfect for the first line of your space bar code. It will tell you what door you want to open or what switch you want to flip. cansee *'Arguments:' (long x1, long y1, long z1, short sectnum1, long x2, long y2, long z2, short sectnum2); returns 0 or 1 *'Function:' This function determines whether or not two 3D points can "see" each other or not. All you do is pass it the coordinates of a 3D line defined by two 3D points (with their respective sectors) The function will return a 1 if the points can see each other or a 0 if there is something blocking the two points from seeing each other. This is how I determine whether a monster can see you or not. Try playing DOOM1.DAT to fully enjoy this great function! setanimation *'Arguments:' (long *animptr, long thegoal, long thevel); *'Function:' This is a function for your convenience that will animate a long variable, such as sector[].floorz for platforms, or sector[].ceilingz for doors. All you do is pass it the long pointer into memory, specifying which long variable is to be animated; you also pass the goal (long value to animate towards), and the velocity at which the variable is animated. Velocity = 128 is a normal speed door. You may also modify the animation arrays directly if you wish: The animation arrays are as follows: *long *animateptrMAXANIMATES, animategoalMAXANIMATES; *long animatevelMAXANIMATES, animatecnt; getanimationgoal *'Arguments:' (long animptr); *'Function:' Check to see if a certain variable in memory is already being animated by the engine. If so, an index into the animation arrays is returned, else -1 is returned. This is function is useful when you are press space bar near a door, and it is already animating, you simply want to reverse its direction. dragpoint *'Arguments:' (short wallnum, long newx, long newy); *'Function:' This function will drag a point in exactly the same way a point is dragged in 2D EDIT MODE using the left mouse button. Simply pass it which wall to drag and then pass the new x and y coordinates for that point. Please use this function because if you don't and try to drag points yourself, I can guarantee that it won't work as well as mine and you will get confused. Note: Every wall of course has 2 points. When you pass a wall number to this function, you are actually passing 1 point, the left side of the wall (given that you are in the sector of that wall). Got it? nextsectorneighborz *'Arguments:' (short sectnum, long thez, short topbottom, short direction); *'Function:' This function searches z-coordinates of neighboring sectors to find the closest (next) ceiling starting at the given z-coordinate (thez). For example, if you want to find the goal z-coordinate when opening a door, you might want the door to stop at the next closest neighboring ceiling z-coordinate. You can get the z-coordinate this way: newz = sectornextsectorneighborz(sectnum,startz,-1,-1).ceilingz topbottom (3rd parameter): *-1 = search ceilings *1 = search floors direction (4th parameter): *-1 = search upwards *1 = search downwards screencapture *'Argument:' (char *filename); *'Function:' Capture the screen and save it as a .BMP file. I don't know why my .BMP format isn't compatible with other programs. = Sprite functions = insertsprite Arguments: (short sectnum, short statnum); //returns (short)spritenum; Whenever you insert a sprite, you must pass it the sector number, and a status number (statnum). The status number can be any number from 0 to MAXSTATUS-1. Insertsprite works like a memory allocation function and returns the sprite number. deletesprite Arguments: (short spritenum); Deletes the sprite. changespritesect Arguments: (short spritenum, short newsectnum); Function: Changes the sector of sprite (spritenum) to the newsector (newsectnum). This function may become internal to the engine in the movesprite function. But this function is necessary since all the sectors have their own doubly-linked lists of sprites. changespritestat Arguments: (short spritenum, short newstatnum); Function: Changes the status of sprite (spritenum) to status (newstatus). Newstatus can be any number from 0 to MAXSTATUS-1. You can use this function to put a monster on a list of active sprites when it first sees you. setsprite Arguments: (short spritenum, long newx, long newy, long newz); Function: This function simply sets the sprite's position to a specified coordinate (newx, newy, newz) without any checking to see whether the position is valid or not. You could directly modify the sprite[].x, sprite[].y, and sprite[].z values, but if you use my function, the sprite is guaranteed to be in the right sector. movesprite Arguments: (short spritenum, long xchange, long ychange, long zchange, long ceildist, long flordist, char cliptype, long numtics) Function: This function moves the sprite given by spritenum by the 3 increments, xchange, ychange, and zchange. If cliptype is 0, then the clipping is normal (Use 0 to clip you and monsters). If the cliptype is 1, then the object is clipped to the same things that hitscan is clipped to (use 1 for all bullets). Movesprite can either return: *0 (touched nothing) *16384+sectnum (ceiling/floor first touched) *32768+wallnum (wall first touched) *49152+spritenum (sprite first touched) getspritescreencoord Arguments: (short spritesortnum, long *scrx, long *scry) Function: This function returns the actual screen coordinates of a sprite. It is useful for locking on to a target. Use this function between drawrooms and drawmasks. Note that spritesortnum is the index into the spritesortcnt arrays, NOT the normal sprite arrays. Scrx and scry are actual screen coordinates ranging from 0-319 and 0-199 respectively. = Multiplayer functions (multi.obj) = initmultiplayers Arguments: (char option4, char option5, char priority); Call this right after initengine. Pass option4 and option5 exactly the way I have it written here. (option4 is the COM1-COM4, network option and option5 is the com speed selection option). Priority can be used to decide who becomes master. Lower is more towards the master. uninitmultiplayers Arguments: (); Function: Call this right before uninitengine. sendlogon *'Arguments:' (); *'Function:' Use this function after everything's initialized, but before you go into the main game loop. Right after you call sendlogon(), you should run a loop that will wait until a specified number of players. Here's some example code: sendlogon(); while (numplayers < waitplayers){ getpackets(); } screenpeek = myconnectindex; Getpackets reserves the packet header range from 200-255. If you keep calling getpackets after sendlogon, the numplayers variable will automatically be incremented when other people log on. sendlogoff Arguments: (); Function: Call this before leaving, before uninitializing the multiplayer code. sendpacket Arguments: (short otherconnectindex, char *bufptr, short bufleng) Function: For COM(modem) communications, the otherconnectindex doesn't matter. For network communcations, you can specify which computer to send to by setting otherconnectindex to the proper index number. You can also do a broadcast by setting otherconnectindex to -1. Also pass the buffer and length parameters. short getpacket Arguments: (short *otherconnectindex, char *bufptr) returns bufleng Function: When using getpacket, first check the value it returns. If the value is 0, then the buffer length is 0 which means there are no packets available. If the buffer length is greater than 0, then use that value as the length of the buffer. Getpacket also tells you what computer the message was received from -(otherconnectindex). = Digitized Sound Functions = Note: If you want to write your own digitized sound driver, simply delete these functions from GAME.C. initsb Arguments: (); Function: Initializes the digitized sound routines. You need to call this only once at the beginning of the program. Currently, the sample rate is 11,025 Hz. wsay Arguments: (char *filename, long freq, char volume); Function: Play the sound file at the given frequency and volume. If you set freq = 4096, the sound will play at normal frequency (given the sound was also recorded at 11025 Hz) To play the sound an octave higher, for example, set freq = 8192. Volume ranges from 0 (silent) to 255 (full volume). Ex: wsay("blowup.wav",4096L,255); uninitsb Arguments: (); Function: Turns the speaker off, so sounds don't continue playing while your back in DOS. = Music functions = Note: If you want to write your own music driver, simply delete these functions from GAME.C. The BUILD engine uses interrupt vector 0x8 (IRQ0)and that may complicate things. If you like my music, perhaps I can send you my MIDI sequencer program (It requires MPU-401, and TSENG-ET4000 SVGA, but could be standardized if there's enough demand). loadmusic Arguments: (char *filename); Function: Loads the given song into memory. Be sure INSTS.DAT is in the current directory. If no extension is given, then .KSM will be appended to the filename. You should use this function only when the music is off. musicon Arguments: (); (none) Function: Enable the playing of music. Use this only after loadmusic has been called. musicoff Arguments: (); (none) Function: Disable the playing of music. Be sure to call this before quitting to DOS. = Links for getting the source code = *Link for downloading Build engine source code (on Ken Silverman homepage) *((Link for downloading Duke Nukem 3d source code on 3drealms needed)) *((also it would be desirable to upload it on the wiki)).